package com.hontech.kaltaapp.device

import com.hontech.kaltaapp.device.codec.ByteView
import com.hontech.kaltaapp.device.codec.Uint16
import com.hontech.kaltaapp.device.codec.u16
import com.hontech.kaltaapp.device.codec.u32
import com.hontech.kaltaapp.device.codec.u8
import com.hontech.kaltaapp.device.event.status
import com.hontech.kaltaapp.ui.dialog.ProgDialog
import kotlinx.coroutines.delay
import java.security.MessageDigest


suspend fun ota(pkg: ByteArray, cb: (Int)->Unit) {
    val size = pkg.size
    Conn.simpleReq(Cmd.OTA_START, arrayOf(size.u32()), arrayOf())
    var index = 0
    var id = 0
    while (index < size) {
        var n = size - index
        if (n > 220) {
            n = 220
        }
        val bw = ByteView(pkg, index, n)
        Conn.simpleReq(Cmd.OTA_TRANSLATE, arrayOf(id.u16(), bw), arrayOf())
        index += n
        id ++
        cb(index * 100 / size)
    }
    val md5 = MessageDigest.getInstance("MD5")
    val sum = md5.digest(pkg)
    Conn.simpleReq(Cmd.OTA_COMPLETE, arrayOf(ByteView(sum, 0, sum.size)), arrayOf())
}


/**
 * 0: 煮面室LED
 * 1: 煮面室风扇
 * 2: 散热风扇
 * 3: 排水泵
 * 4: 电磁阀1
 * 5: 电磁阀2
 * 6: 电磁阀3
 * 7: 电磁阀4
 * 8: 电磁阀5
 * 9: 电磁阀6
 * 10: 电磁阀7
 * 11: 电磁阀8
 * 12:开水加热器
 * 13: 蒸汽加热器
 * 14: 抽水泵
 */
suspend fun partsCtrl(id: Int, value: Int) {
    Conn.simpleReq(Cmd.PARTS_CTRL, arrayOf(id.u8(), value.u8()), arrayOf())
}


suspend fun nozzleCtrl(mm: Int) {
    Conn.req(Cmd.NOZZLE_CTRL, arrayOf(mm.u16()), arrayOf())
}

suspend fun doorCtrl(value: Int) {
    Conn.req(Cmd.DOOR_CTRL, arrayOf(value.u8()), arrayOf())
}

suspend fun scan(): String {
    val bw = ByteView()
    Conn.req(Cmd.SCAN, arrayOf(), arrayOf(bw))
    return bw.toString()
}

/**
 * 0: 流量计参数
 * 1: 蒸汽加热温度
 * 2: 蒸汽加热压力
 * 3: 蒸汽加热超时时间
 * 4: 瞬热器加热温度
 * 5: 瞬热器加热超时时间
 * 6: 抽水超时时间
 */
suspend fun setParam(id: Int, value: Int) {
    Conn.req(Cmd.PARAM, arrayOf(id.u8(), value.u16()), arrayOf())
}

suspend fun getParam(id: Int): Int {
    val v = Uint16()
    Conn.simpleReq(Cmd.PARAM, arrayOf(id.u8()), arrayOf(v))
    return v.value
}

/**
 * 0: 抽水重启
 * 1: 抽水停止
 * 2: 瞬热器加热重启
 * 3:瞬热器加热停止
 * 4: 锅炉加热重启
 * 5: 锅炉加热停机
 */
suspend fun drawHeatCtrl(v: Int) {
    Conn.simpleReq(Cmd.DRAW_HEAT_CTRL, arrayOf(v.u8()), arrayOf())
}

suspend fun drawWater(mm: Int, temp: Int, ml: Int) {
    Conn.req(Cmd.DRAW_WATER, arrayOf(mm.u16(), temp.u8(), ml.u16()), arrayOf())
}

suspend fun drawWater2(ml: Int) {
    Conn.req(Cmd.DRAW_WATER2, arrayOf(ml.u16()), arrayOf())
}


const val WAIT_KPA_READY_TIMEOUT = 30 * 1000

private suspend fun waitKpaReady(kpa: Int) {
    var timeCount = 0L
    drawHeatCtrl(4)
    while (status().kpa < kpa) {
        delay(600)
        timeCount += 600
        if (timeCount >= WAIT_KPA_READY_TIMEOUT) {
            throw IllegalStateException("等待压力就绪超时")
        }
    }
}

class CleanInfo(
    val kpa: Int,
    val c: Int,
    val d: Int,
    val water: Int,
    val steam: Int
)

private suspend fun cleanImpl(info: CleanInfo, dialog: ProgDialog) {
    if (status().sensorStatus.bowlChecked) {
        throw IllegalStateException("里面有碗")
    }
    if (!status().sensorStatus.doorIsClose) {
        throw IllegalStateException("门没有关")
    }

    dialog.setMsg("等待压力就绪")
    waitKpaReady(info.kpa)

    dialog.setMsg("放水")
    nozzleCtrl(info.c)
    drawWater2(info.water)

    dialog.setMsg("放蒸汽")
    try {
        partsCtrl(7, 0)
        partsCtrl(8, 0)
        var timeCount = 0L
        while (timeCount < (info.steam * 1000)) {
            val start = System.currentTimeMillis()
            nozzleCtrl(info.d)
            nozzleCtrl(info.c)
            timeCount += System.currentTimeMillis() - start
        }
    } finally {
        partsCtrl(8, 1)
        partsCtrl(7, 1)
    }
}

suspend fun clean(info: CleanInfo, dialog: ProgDialog) {
    cleanImpl(info, dialog)
    dialog.setMsg("复位")
    nozzleCtrl(0)
}


class CookingInfo(
    val k: Int,
    val preWater: Int,
    val f: Int,
    val g: Int,
    val defroze: Int,
    val e: Int,
    val pourWater: Int,
    val h: Int,
    val mixSoup: Int,
    val i: Int,
    val clogging: Int
)

private const val WAIT_BOWL_TIMEOUT = 30 * 1000
private suspend fun waitBowl() {
    var count = 0
    while (!status().sensorStatus.bowlChecked) {
        delay(600)
        count += 600
        if (count >= WAIT_BOWL_TIMEOUT) {
            throw IllegalStateException("放碗超时")
        }
    }
}

private suspend fun waitBowlDrop() {
    var count = 0
    while (status().sensorStatus.bowlChecked) {
        delay(600)
        count += 600
        if (count >= WAIT_BOWL_TIMEOUT) {
            throw IllegalStateException("放碗超时")
        }
    }
}

private const val WAIT_CLOSE_DOOR_TIMEOUT = 30 * 1000
private suspend fun waitCloseDoor() {
    var count = 0
    while (!status().sensorStatus.doorIsClose) {
        delay(600)
        count += 600
        if (count >= WAIT_CLOSE_DOOR_TIMEOUT) {
            throw IllegalStateException("关门超时")
        }
    }
}

suspend fun cooking(cleanInfo: CleanInfo, cookingInfo: CookingInfo, dialog: ProgDialog) {
    clean(cleanInfo, dialog)

    dialog.setMsg("等待放置碗")
    waitBowl()

    dialog.setMsg("等待关门")
    waitCloseDoor()

    if (cookingInfo.preWater != 0) {
        dialog.setMsg("Pre Water")
        nozzleCtrl(cookingInfo.k)
        drawWater2(cookingInfo.preWater)
    }

    if (cookingInfo.defroze != 0) {
        dialog.setMsg("Defroze")
        var timeCount = 0L
        try {
            partsCtrl(7, 0)
            while (timeCount < (cookingInfo.defroze * 1000)) {
                val start = System.currentTimeMillis()
                nozzleCtrl(cookingInfo.f)
                nozzleCtrl(cookingInfo.g)
                timeCount += System.currentTimeMillis() - start
            }
        } finally {
            partsCtrl(7, 1)
        }
    }

    if (cookingInfo.pourWater != 0) {
        dialog.setMsg("Pour Water")
        nozzleCtrl(cookingInfo.e)
        drawWater2(cookingInfo.pourWater)
    }

    if (cookingInfo.mixSoup != 0) {
        dialog.setMsg("Mix Soup")
        nozzleCtrl(cookingInfo.h)
        partsCtrl(7, 0)
        delay(cookingInfo.mixSoup * 1000L)
        partsCtrl(7, 1)
    }

    if (cookingInfo.clogging != 0) {
        dialog.setMsg("Clogging")
        nozzleCtrl(cookingInfo.i)
        partsCtrl(7, 0)
        delay(cookingInfo.clogging * 1000L)
        partsCtrl(7, 1)
    }

    dialog.setMsg("复位")
    nozzleCtrl(0)

    dialog.setMsg("请拿碗")
    waitBowlDrop()
    dialog.setMsg("请关门")
    waitCloseDoor()
    clean(cleanInfo, dialog)
}

































